<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="本文主要是字符设备驱动基础的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV10-02-字符设备驱动-01-基础知识">
<meta property="og:url" content="https://sumumm.github.io/post/f0272df3.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="本文主要是字符设备驱动基础的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/image-20220831152808171.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/image-20220831153510754.png">
<meta property="article:published_time" content="2023-07-08T10:58:07.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.002Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="LV10-驱动开发">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/image-20220831152808171.png">


<link rel="canonical" href="https://sumumm.github.io/post/f0272df3.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://sumumm.github.io/post/f0272df3.html","path":"post/f0272df3.html","title":"LV10-02-字符设备驱动-01-基础知识"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV10-02-字符设备驱动-01-基础知识 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E7%AE%80%E4%BB%8B"><span class="nav-text">一、字符设备驱动简介</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%EF%BC%9F"><span class="nav-text">1. 字符设备？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%A6%82%E4%BD%95%E8%B0%83%E7%94%A8%E9%A9%B1%E5%8A%A8%EF%BC%9F"><span class="nav-text">2. 如何调用驱动？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-struct-file-operations"><span class="nav-text">3. struct file_operations </span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E5%8A%A0%E8%BD%BD%E4%B8%8E%E5%8D%B8%E8%BD%BD"><span class="nav-text">二、字符设备加载与卸载</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%8A%A0%E8%BD%BD%E4%B8%8E%E5%8D%B8%E8%BD%BD%E6%A8%A1%E6%9D%BF"><span class="nav-text">1. 加载与卸载模板</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E6%B7%BB%E5%8A%A0LICENSE"><span class="nav-text">2. 添加LICENSE</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81%E8%AE%BE%E5%A4%87%E5%8F%B7%E5%88%86%E9%85%8D"><span class="nav-text">三、设备号分配</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E8%AE%BE%E5%A4%87%E5%8F%B7%E7%9A%84%E7%BB%84%E6%88%90"><span class="nav-text">1. 设备号的组成</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E7%9B%B8%E5%85%B3%E5%87%BD%E6%95%B0"><span class="nav-text">2. 相关函数</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E8%AE%BE%E5%A4%87%E5%8F%B7%E7%9A%84%E5%88%86%E9%85%8D"><span class="nav-text">3. 设备号的分配</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E9%9D%99%E6%80%81%E5%88%86%E9%85%8D%E8%AE%BE%E5%A4%87%E5%8F%B7"><span class="nav-text">3.1 静态分配设备号</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-1-1-%E6%9F%A5%E7%9C%8B%E5%B7%B2%E4%BD%BF%E7%94%A8%E7%9A%84%E4%B8%BB%E8%AE%BE%E5%A4%87%E5%8F%B7"><span class="nav-text">3.1.1 查看已使用的主设备号</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-1-2-%E6%B3%A8%E5%86%8C%E9%9D%99%E6%80%81%E5%88%86%E9%85%8D%E7%9A%84%E8%AE%BE%E5%A4%87%E5%8F%B7"><span class="nav-text">3.1.2 注册静态分配的设备号</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D%E8%AE%BE%E5%A4%87%E5%8F%B7"><span class="nav-text">3.2 动态分配设备号</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-1-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D"><span class="nav-text">3.2.1 为什么要动态分配</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-2-%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D%E5%87%BD%E6%95%B0"><span class="nav-text">3.2.2 动态分配函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E8%AE%BE%E5%A4%87%E5%8F%B7%E9%87%8A%E6%94%BE"><span class="nav-text">4. 设备号释放</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-%E7%9B%B8%E5%85%B3%E5%87%BD%E6%95%B0"><span class="nav-text">4.1 相关函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9B%E3%80%81%E8%AE%BE%E5%A4%87%E6%B3%A8%E5%86%8C"><span class="nav-text">四、设备注册</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E7%BB%93%E6%9E%84"><span class="nav-text">1. 字符设备结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0%E9%9B%86"><span class="nav-text">2. 字符设备操作函数集</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E5%88%9D%E5%A7%8B%E5%8C%96%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E5%8F%98%E9%87%8F"><span class="nav-text">3. 初始化字符设备变量</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-owner%E6%88%90%E5%91%98"><span class="nav-text">3.1 owner成员</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-cdev-init"><span class="nav-text">3.2 cdev_init()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-cdev-add"><span class="nav-text">3.3 cdev_add()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-4-cdev-del"><span class="nav-text">3.4 cdev_del()</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%94%E3%80%81%E8%AE%BE%E5%A4%87%E8%8A%82%E7%82%B9%E5%88%9B%E5%BB%BA"><span class="nav-text">五、设备节点创建</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E6%89%8B%E5%8A%A8%E5%88%9B%E5%BB%BA%E8%AE%BE%E5%A4%87%E8%8A%82%E7%82%B9"><span class="nav-text">1. 手动创建设备节点</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%88%9B%E5%BB%BA%E8%AE%BE%E5%A4%87%E8%8A%82%E7%82%B9"><span class="nav-text">2. 应用程序创建设备节点</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-mknod"><span class="nav-text">2.1 mknod()</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E8%87%AA%E5%8A%A8%E5%88%9B%E5%BB%BA%E8%AE%BE%E5%A4%87%E8%8A%82%E7%82%B9"><span class="nav-text">3. 自动创建设备节点</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-mdev%E6%9C%BA%E5%88%B6"><span class="nav-text">3.1 mdev机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-%E5%88%9B%E5%BB%BA%E5%92%8C%E5%88%A0%E9%99%A4%E7%B1%BB"><span class="nav-text">3.2 创建和删除类</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-1-class-create"><span class="nav-text">3.2.1 class_create()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-2-class-destroy"><span class="nav-text">3.2.2 class_destroy()</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3%E5%88%9B%E5%BB%BA%E5%92%8C%E5%88%A0%E9%99%A4%E8%AE%BE%E5%A4%87"><span class="nav-text">3.3创建和删除设备</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-3-1-device-create"><span class="nav-text">3.3.1 device_create()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-2-device-destroy"><span class="nav-text">3.2.2 device_destroy()</span></a></li></ol></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/f0272df3.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV10-02-字符设备驱动-01-基础知识 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV10-02-字符设备驱动-01-基础知识
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-07-08 18:58:07" itemprop="dateCreated datePublished" datetime="2023-07-08T18:58:07+08:00">2023-07-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/" itemprop="url" rel="index"><span itemprop="name">01HQ课程体系</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">LV10-驱动开发</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>8.4k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>31 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>本文主要是字符设备驱动基础的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" width=150px>Windows</td>        <td align="left">windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu16.04的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 16 Pro</td>        <td align="left">16.2.3 build-19376536</td>      </tr>    <tr>        <td align="center">SecureCRT</td>        <td align="left">Version 8.7.2 (x64 build 2214)   -   正式版-2020年5月14日</td>      </tr>    <tr>        <td align="center" width=150>Linux开发板</td>        <td align="left">华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2</td>      </tr>    <tr>        <td align="center" width=150>u-boot</td>        <td align="left">2013.01</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr><td align="center">参考方向  </td><td align="center">参考原文</td></tr>    <tr><td align="left">---</td><td align="left"><a href="" target="_blank">--- <i class="fa fa-external-link-alt"></i> </a></td></tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr><td align="center">文件</td><td align="center">下载链接</td></tr>    <tr><td align="left">---</td><td align="left"><a href="https://wwz.lanzouy.com/iH1vO09jxavi" target="_blank">--- <i class="fa fa-external-link-alt"></i> </a></td></tr></table>
              </div>
            </details>



<h1 id="一、字符设备驱动简介"><a href="#一、字符设备驱动简介" class="headerlink" title="一、字符设备驱动简介"></a><font size=3>一、字符设备驱动简介</font></h1><h2 id="1-字符设备？"><a href="#1-字符设备？" class="headerlink" title="1. 字符设备？"></a><font size=3>1. 字符设备？</font></h2><p>字符设备是<code>Linux</code>驱动中最基本的一类设备驱动，字符设备就是一个一个字节，<strong>按照字节流进行读写操作</strong>的设备，读写数据是分先后顺序的。比如我们最常见的<code>LED</code>灯、按键、 <code>IIC</code>、 <code>SPI</code>，<code>LCD</code> 等等都是字符设备，这些设备的驱动就叫做字符设备驱动。  </p>
<h2 id="2-如何调用驱动？"><a href="#2-如何调用驱动？" class="headerlink" title="2. 如何调用驱动？"></a><font size=3>2. 如何调用驱动？</font></h2><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/image-20220831152808171.png" alt="image-20220831152808171" style="zoom:43%;" />

<p>驱动加载成功以后会在<code>/dev</code>目录下生成一个相应的文件，应用程序通过对这个名为<code>/dev/xxx</code>(<code>xxx</code> 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。</p>
<p>比如现在有个叫做<code>/dev/led</code>的驱动文件，此文件是<code>led</code>灯的驱动文件。应用程序使用<code>open</code>函数来打开文件<code>/dev/led</code>，使用完成以后使用<code>close</code>函数关闭<code>/dev/led</code>这个文件。 <code>open</code>和<code>close</code>就是打开和关闭 <code>led</code>驱动的函数，如果要点亮或关闭 <code>led</code>，那么就使用 <code>write</code> 函数来操作，也就是向此驱动写入数据，这个数据就是要关闭还是要打开<code>led</code>的控制参数。如果要获取<code>led</code>灯的状态，就用 <code>read</code>函数从驱动中读取相应的状态。</p>
<p>应用程序运行在用户空间，而<code>Linux</code>驱动属于内核的一部分，因此驱动运行于内核空间。当我们在用户空间想要实现对内核的操作，比如使用<code>open</code>函数打开<code>/dev/led</code>这个驱动，因为用户空间不能直接对内核进行操作，因此必须使用一个叫做<strong>系统调用</strong>的方法来实现从用户空间进入到内核空间，这样才能实现对底层驱动的操作。 <code>open</code>、 <code>close</code>、<code>write</code> 和 <code>read</code> 等这些函数是由 <code>C</code> 库提供的，在 <code>Linux</code> 系统中，系统调用作为 <code>C</code> 库的一部分。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV10-02-%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8-01-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/img/image-20220831153510754.png" alt="image-20220831153510754" style="zoom:50%;" />

<p>如上图所示，在应用中调用了<code>open</code>函数，然后对应一个关于<code>open</code>的系统调用，在这个系统调用中，再调用我们编写的驱动函数，应用程序中怎么找到这个系统调用，以及如何进入内核空间的，暂时不用关心，我们需要关心的是这个<code>open</code>系统调用怎么知道要调用驱动中的<code>myopen</code>呢？</p>
<h2 id="3-struct-file-operations"><a href="#3-struct-file-operations" class="headerlink" title="3. struct file_operations "></a><font size=3>3. <code>struct file_operations </code></font></h2><p>每一个系统调用，在驱动中都有与之对应的一个驱动函数，他们之间是通过一个结构体联系起来的，在<code>Linux</code>内核文件<code> include/linux/fs.h</code>中有一个名为<code>file_operations</code> 的结构体，此结构体就是 <code>Linux</code> 内核驱动操作函数集合。</p>
<details class="folding-tag" blue><summary> 点击查看 struct file_operations 结构体成员 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">module</span> *<span class="title">owner</span>;</span></span><br><span class="line">	<span class="type">loff_t</span> (*llseek) (<span class="keyword">struct</span> file *, <span class="type">loff_t</span>, <span class="type">int</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*read) (<span class="keyword">struct</span> file *, <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);</span><br><span class="line">	<span class="type">ssize_t</span> (*write) (<span class="keyword">struct</span> file *, <span class="type">const</span> <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);</span><br><span class="line">	<span class="type">ssize_t</span> (*aio_read) (<span class="keyword">struct</span> kiocb *, <span class="type">const</span> <span class="keyword">struct</span> iovec *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">loff_t</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*aio_write) (<span class="keyword">struct</span> kiocb *, <span class="type">const</span> <span class="keyword">struct</span> iovec *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">loff_t</span>);</span><br><span class="line">	<span class="type">int</span> (*iterate) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> dir_context *);</span><br><span class="line">	<span class="type">unsigned</span> <span class="title function_">int</span> <span class="params">(*poll)</span> <span class="params">(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> poll_table_struct *)</span>;</span><br><span class="line">	<span class="type">long</span> (*unlocked_ioctl) (<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">long</span>);</span><br><span class="line">	<span class="type">long</span> (*compat_ioctl) (<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">long</span>);</span><br><span class="line">	<span class="type">int</span> (*mmap) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> vm_area_struct *);</span><br><span class="line">	<span class="type">int</span> (*open) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *);</span><br><span class="line">	<span class="type">int</span> (*flush) (<span class="keyword">struct</span> file *, <span class="type">fl_owner_t</span> id);</span><br><span class="line">	<span class="type">int</span> (*release) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *);</span><br><span class="line">	<span class="type">int</span> (*fsync) (<span class="keyword">struct</span> file *, <span class="type">loff_t</span>, <span class="type">loff_t</span>, <span class="type">int</span> datasync);</span><br><span class="line">	<span class="type">int</span> (*aio_fsync) (<span class="keyword">struct</span> kiocb *, <span class="type">int</span> datasync);</span><br><span class="line">	<span class="type">int</span> (*fasync) (<span class="type">int</span>, <span class="keyword">struct</span> file *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*lock) (<span class="keyword">struct</span> file *, <span class="type">int</span>, <span class="keyword">struct</span> file_lock *);</span><br><span class="line">	<span class="type">ssize_t</span> (*sendpage) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> page *, <span class="type">int</span>, <span class="type">size_t</span>, <span class="type">loff_t</span> *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">unsigned</span> <span class="title function_">long</span> <span class="params">(*get_unmapped_area)</span><span class="params">(<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>)</span>;</span><br><span class="line">	<span class="type">int</span> (*check_flags)(<span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*flock) (<span class="keyword">struct</span> file *, <span class="type">int</span>, <span class="keyword">struct</span> file_lock *);</span><br><span class="line">	<span class="type">ssize_t</span> (*splice_write)(<span class="keyword">struct</span> pipe_inode_info *, <span class="keyword">struct</span> file *, <span class="type">loff_t</span> *, <span class="type">size_t</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*splice_read)(<span class="keyword">struct</span> file *, <span class="type">loff_t</span> *, <span class="keyword">struct</span> pipe_inode_info *, <span class="type">size_t</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*setlease)(<span class="keyword">struct</span> file *, <span class="type">long</span>, <span class="keyword">struct</span> file_lock **);</span><br><span class="line">	<span class="type">long</span> (*fallocate)(<span class="keyword">struct</span> file *file, <span class="type">int</span> mode, <span class="type">loff_t</span> offset,<span class="type">loff_t</span> len);</span><br><span class="line">	<span class="type">int</span> (*show_fdinfo)(<span class="keyword">struct</span> seq_file *m, <span class="keyword">struct</span> file *f);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
              </div>
            </details>

<p><strong>【常用成员说明】</strong></p>
<ul>
<li><code>owner</code>：填<code>THIS_MODULE</code>，表示该结构体对象从属于哪个内核模块。</li>
<li><code>llseek</code>：<code>llseek</code>函数用于修改文件当前的读写位置。</li>
<li><code>read</code>：<code>read</code> 函数用于读取设备文件，与应用程序中的 <code>read()</code> 函数对应。</li>
<li><code>write</code>：<code>write</code> 函数用于向设备文件写入(发送)数据，与应用程序中的 <code>write()</code> 函数对应。</li>
<li><code>poll</code>：<code>poll</code> 是个轮询函数，用于查询设备是否可以进行非阻塞的读写，实现多路复用的支持。</li>
<li><code>unlocked_ioctl</code>：<code>unlocked_ioctl</code> 函数提供对于设备的控制功能，如读写设备参数，读设备状态等，与应用程序中的 <code>ioctl()</code> 函数对应。</li>
<li><code>compat_ioctl</code>与<code>unlocked_ioctl </code>函数功能一样，区别在于在 <code>64</code> 位系统上，<code>32</code> 位的应用程序调用将会使用此函数。在 <code>32</code> 位的系统上运行<code>32</code>位的应用程序调用的是<code>unlocked_ioctl</code>。</li>
<li><code>mmap</code>：用于将将设备的内存映射到进程空间中（也就是用户空间，简单来说就是映射内核空间到用户空间），一般帧缓冲设备会使用此函数，比如 <code>LCD</code> 驱动的显存，将帧缓冲（<code>LCD</code> 显存）映射到用户空间中以后应用程序就可以直接操作显存了，这样就不用在用户空间和内核空间之间来回复制。</li>
<li><code>open</code>：用于打开设备文件，与应用程序中的<code>open()</code>函数对应。</li>
<li><code>release</code>：用于释放(关闭)设备文件，与应用程序中的<code>close()</code>函数对应。</li>
<li><code>fasync</code>：用于常用于异步通知，如信号驱动。</li>
</ul>
<p>一般我们会定义一个<code>struct file_operations</code>类型的全局变量并用自己实现的各种操作函数名对其进行初始化，如<code>myopen()</code>。初始化后，由于<strong>该结构体对象各个函数指针成员都对应相应的系统调用函数</strong>，<strong>应用层通过调用系统函数来间接调用这些函数指针成员指向的设备驱动函数</strong>，这样当我们在应用程序中调用<code>open</code>的时候，系统调用就会在内部调用相应的<code>myopen</code>驱动函数了。</p>
<h1 id="二、字符设备加载与卸载"><a href="#二、字符设备加载与卸载" class="headerlink" title="二、字符设备加载与卸载"></a><font size=3>二、字符设备加载与卸载</font></h1><h2 id="1-加载与卸载模板"><a href="#1-加载与卸载模板" class="headerlink" title="1. 加载与卸载模板"></a><font size=3>1. 加载与卸载模板</font></h2><p>这是驱动开发的最基本的步骤，模块有加载和卸载两种操作，我们在编写驱动的时候需要注册这两种操作函数，模块的加载和<br>卸载注册函数如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">module_init(xxx_init); <span class="comment">//注册模块加载函数</span></span><br><span class="line">module_exit(xxx_exit); <span class="comment">//注册模块卸载函数</span></span><br></pre></td></tr></table></figure>

<p><code>module_init</code>函数用来向<code>Linux</code>内核注册一个模块加载函数，参数<code>xxx_init</code>就是需要注册的具体函数，当使用<code>insmod</code>命令加载驱动的时候，<code>xxx_init</code>这个函数就会被调用。<code>module_exit()</code>函数用来向<code>Linux</code>内核注册一个模块卸载函数，参数<code>xxx_exit</code>就是需要注册的具体函数，当使用<code>rmmod</code>命令卸载具体驱动的时候<code>xxx_exit</code>函数就会被调用。  </p>
<ul>
<li>字符设备加载与卸载模板</li>
</ul>
<details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 模块入口函数 */</span></span><br><span class="line"><span class="type">int</span> __init <span class="title function_">xxx_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">/* 入口函数具体内容 */</span></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/* 模块出口函数 */</span></span><br><span class="line"><span class="type">void</span> __exit <span class="title function_">xxx_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">/* 出口函数具体内容 */</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/* 将上面两个函数指定为驱动的入口和出口函数 */</span></span><br><span class="line">module_init(xxx_init); <span class="comment">/* 注册模块加载函数 */</span></span><br><span class="line">module_exit(xxx_exit);  <span class="comment">/* 注册模块卸载函数 */</span></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h2 id="2-添加LICENSE"><a href="#2-添加LICENSE" class="headerlink" title="2. 添加LICENSE"></a><font size=3>2. 添加<code>LICENSE</code></font></h2><p>前边我们已经了解过模块信息宏的相关概念，模块信息中这个<code>LICENSE</code>是必须要添加的，还可以添加上作者名字，一般为：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 模块信息(通过 modinfo dev_name 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL&quot;</span>);               <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;qidaink&quot;</span>);            <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br></pre></td></tr></table></figure>

<h1 id="三、设备号分配"><a href="#三、设备号分配" class="headerlink" title="三、设备号分配"></a><font size=3>三、设备号分配</font></h1><h2 id="1-设备号的组成"><a href="#1-设备号的组成" class="headerlink" title="1. 设备号的组成"></a><font size=3>1. 设备号的组成</font></h2><p><code>Linux</code> 中每个设备都有一个设备号，设备号由主设备号和次设备号两部分组成，主设备号表示某一个具体的驱动，次设备号表示使用这个驱动的各个设备，设备号主要是用于<strong>区分内核中同类设备</strong>。 当应用程序打开一个设备文件时，<strong>通过设备号来查找定位内核中管理的设备</strong>。</p>
<p><code>Linux</code> 提供了一个名为<code>dev_t</code>的数据类型表示设备号，<code>dev_t </code>定义在文件<code> include/linux/types.h</code> 里面，定义如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> __u32 <span class="type">__kernel_dev_t</span>;</span><br><span class="line">... ;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">__kernel_dev_t</span> <span class="type">dev_t</span>;</span><br></pre></td></tr></table></figure>

<p><code>dev_t</code> 是<code>__u32</code> 类型的，而<code>__u32</code>定义在文件<code>include/uapi/asm-generic/int-ll64.h</code>里面，定义如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">int</span> __u32;</span><br></pre></td></tr></table></figure>

<p>所以<code>dev_t</code> 其实就是 <code>unsigned int</code> 类型，是一个 <code>32</code> 位的数据类型。 这<code>32</code>位包含了主设备号和次设备号：</p>
<ul>
<li>主设备号：占高<code>12</code>位，用来表示驱动程序相同的一类设备，因此<code>Linux</code>系统中主设备号范围为<code>0~4095</code>。</li>
<li>次设备号：占低<code>20</code>位，用来表示被操作的哪个具体设备。</li>
</ul>
<h2 id="2-相关函数"><a href="#2-相关函数" class="headerlink" title="2. 相关函数"></a><font size=3>2. 相关函数</font></h2><p>对于设备号，<code>linux</code>内核中的<code>include/linux/kdev_t.h</code>文件中为我们提供了如下几个函数（本质是宏）来操作设备号：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MINORBITS       20</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MINORMASK       ((1U &lt;&lt; MINORBITS) - 1)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAJOR(dev)      ((unsigned int) ((dev) &gt;&gt; MINORBITS))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MINOR(dev)      ((unsigned int) ((dev) &amp; MINORMASK))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MKDEV(ma,mi)    (((ma) &lt;&lt; MINORBITS) | (mi))</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>MINORBITS</code>：表示次设备号位数，一共是<code>20</code>位。  </li>
<li><code>MINORMASK</code>：表示次设备号掩码。  </li>
<li><code>MAJOR</code>： 用于从<code>dev_t</code>中获取主设备号，将<code>dev_t</code>右移<code>20</code>位即可。例如，</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">dev_t</span> devno = MKDEV(<span class="number">999</span>,<span class="number">1</span>);</span><br><span class="line"><span class="type">int</span> major = MAJOR(devno);</span><br></pre></td></tr></table></figure>

<ul>
<li><code>MINOR</code>：用于从<code>dev_t</code>中获取次设备号，取<code>dev_t</code>的低<code>20</code>位的值即可。例如，</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">dev_t</span> devno = MKDEV(<span class="number">999</span>,<span class="number">1</span>);</span><br><span class="line"><span class="type">int</span> minor = MINOR(devno);</span><br></pre></td></tr></table></figure>

<ul>
<li><code>MKDEV</code>：用于将给定的主设备号和次设备号的值组合成<code>dev_t </code>类型的设备号。  例如，</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">dev_t</span> devno;</span><br><span class="line"><span class="type">int</span> major = <span class="number">999</span>;<span class="comment">/* 主设备号 */</span></span><br><span class="line"><span class="type">int</span> minor = <span class="number">0</span>;  <span class="comment">/* 次设备号 */</span></span><br><span class="line">devno = MKDEV(major,minor);</span><br></pre></td></tr></table></figure>

<h2 id="3-设备号的分配"><a href="#3-设备号的分配" class="headerlink" title="3. 设备号的分配"></a><font size=3>3. 设备号的分配</font></h2><p>设备号的分配有两种方式，一种是静态分配，一种是动态分配。</p>
<h3 id="3-1-静态分配设备号"><a href="#3-1-静态分配设备号" class="headerlink" title="3.1 静态分配设备号"></a><font size=3>3.1 静态分配设备号</font></h3><p>设备号可以是驱动开发者静态的指定一个设备号，比如选择<code>200</code>这个主设备号。有一些常用的设备号已经被<code>Linux</code>内核开发者给分配掉了，具体分配的内容可以查看文档<code>Documentation/devices.txt</code>。并不是说内核开发者已经分配掉的主设备号我们就不能用了，具体能不能用还得看我们的硬件平台运行过程中有没有使用这个主设备号。</p>
<h4 id="3-1-1-查看已使用的主设备号"><a href="#3-1-1-查看已使用的主设备号" class="headerlink" title="3.1.1 查看已使用的主设备号"></a><font size=3>3.1.1 查看已使用的主设备号</font></h4><p>我们可以使用下边的命令查看当前系统中所有已经使用了的主设备号：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/devices</span><br></pre></td></tr></table></figure>

<p>我们想要查询我们加载的模块的设备号的话，可以加上<code>grep</code>搜索：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/devices | grep 申请设备号时用的名字</span><br></pre></td></tr></table></figure>

<h4 id="3-1-2-注册静态分配的设备号"><a href="#3-1-2-注册静态分配的设备号" class="headerlink" title="3.1.2 注册静态分配的设备号"></a><font size=3>3.1.2 注册静态分配的设备号</font></h4><p>当我们给定了主设备号和次设备号的时候需要进行设备号的注册，我们可以使用<code>register_chrdev_region</code>函数进行静态分配的设备号的注册，我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep register_chrdev_region -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">register_chrdev_region</span><span class="params">(<span class="type">dev_t</span> from, <span class="type">unsigned</span> count, <span class="type">const</span> <span class="type">char</span> *name)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于注册静态分配的设备号，先验证设备号是否被占用，如果没有则申请占用该设备号，分配成功后可以在<code>/proc/devices</code>查看到申请到主设备号和对应的设备名。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>from</code>：<code>dev_t</code>类型，要申请的设备号，也就是给定的设备号。</li>
<li><code>count</code>：<code>unsigned</code>类型，需要申请的设备数量。</li>
<li><code>name</code>：<code>char *</code>类型，传入的是一个字符串，表示<code>/proc/devices</code>文件中与该设备对应的名字（就是设备名称），方便用户层查询主设备号。</li>
</ul>
<p><strong>【返回值】</strong><code>int</code>类型，成功返回<code>0</code>，失败返回一个负数，负数的绝对值表示错误码。</p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kdev_t.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line"><span class="type">int</span> major = <span class="number">11</span>;  <span class="comment">/* 主设备号：占高12位，用来表示驱动程序相同的一类设备 */</span></span><br><span class="line"><span class="type">int</span> minor = <span class="number">0</span>;   <span class="comment">/* 次设备号：占低20位，用来表示被操作的哪个具体设备 */</span></span><br><span class="line"><span class="type">int</span> dev_num = <span class="number">1</span>; <span class="comment">/* 申请的设备数量 */</span></span><br><span class="line"></span><br><span class="line"><span class="type">dev_t</span> devno = MKDEV(major, minor); <span class="comment">/* MKDEV宏用来将主设备号和次设备号组合成32位完整的设备号 */</span></span><br><span class="line">register_chrdev_region(devno, dev_num, <span class="string">&quot;dev_name&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h3 id="3-2-动态分配设备号"><a href="#3-2-动态分配设备号" class="headerlink" title="3.2 动态分配设备号"></a><font size=3>3.2 动态分配设备号</font></h3><h4 id="3-2-1-为什么要动态分配"><a href="#3-2-1-为什么要动态分配" class="headerlink" title="3.2.1 为什么要动态分配"></a><font size=3>3.2.1 为什么要动态分配</font></h4><p>静态分配设备号需要我们检查当前系统中所有被使用了的设备号，然后挑选一个没有使用的。而且静态分配设备号很容易带来冲突问题， <code>Linux </code>社区推荐使用动态分配设备号，在注册字符设备之前先申请一个设备号，系统会自动给我们一个没有被使用的设备号，这样就避免了冲突，卸载驱动的时候释放掉这个设备号即可。</p>
<h4 id="3-2-2-动态分配函数"><a href="#3-2-2-动态分配函数" class="headerlink" title="3.2.2 动态分配函数"></a><font size=3>3.2.2 动态分配函数</font></h4><p>我们可以使用<code>alloc_chrdev_region</code>函数进行静态分配的设备号的注册，我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep alloc_chrdev_region -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">alloc_chrdev_region</span><span class="params">(<span class="type">dev_t</span> *dev, <span class="type">unsigned</span> baseminor, <span class="type">unsigned</span> count, <span class="type">const</span> <span class="type">char</span> *name)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于动态分配设备号，查询内核里未被占用的设备号，如果找到则占用该设备号，分配成功后可以在<code>/proc/devices</code>查看到申请到主设备号和对应的设备名。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>dev</code>：<code>dev_t *</code>类型，传入的是一个地址，保存的是申请到的设备号。</li>
<li><code>baseminor</code>：<code>unsigned</code>类型，次设备号起始地址， 该函数可以申请一段连续的多个设备号，这些设备号的主设备号一样，但是次设备号不同，次设备号以<code>baseminor</code>为起始地址地址开始递增。一般<code>baseminor</code>为<code>0</code>，也就是说次设备号从<code>0</code>开始。  </li>
<li><code>count</code>：<code>unsigned</code>类型，要申请的设备号数量。  </li>
<li><code>name</code>：<code>char *</code>类型，传入的是一个字符串，表示<code>/proc/devices</code>文件中与该设备对应的名字（就是设备名称），方便用户层查询主设备号。</li>
</ul>
<p><strong>【返回值】</strong><code>int</code>类型，成功返回<code>0</code>，失败返回一个负数，负数的绝对值表示错误码。</p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kdev_t.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line"><span class="comment">// int major = 11;  /* 主设备号：占高12位，用来表示驱动程序相同的一类设备 */</span></span><br><span class="line"><span class="type">int</span> minor = <span class="number">0</span>;   <span class="comment">/* 次设备号：占低20位，用来表示被操作的哪个具体设备 */</span></span><br><span class="line"><span class="type">int</span> dev_num = <span class="number">1</span>; <span class="comment">/* 申请的设备数量 */</span></span><br><span class="line"></span><br><span class="line"><span class="type">dev_t</span> devno;</span><br><span class="line">alloc_chrdev_region(&amp;devno, minor, dev_num, <span class="string">&quot;dev_name&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h2 id="4-设备号释放"><a href="#4-设备号释放" class="headerlink" title="4. 设备号释放"></a><font size=3>4. 设备号释放</font></h2><h3 id="4-1-相关函数"><a href="#4-1-相关函数" class="headerlink" title="4.1 相关函数"></a><font size=3>4.1 相关函数</font></h3><p>我们申请的设备号不再使用的时候就需要释放掉这个设备号，以便于其他设备使用，静态分配或者是动态分配的设备号我们统一使用函数<code>unregister_chrdev_region</code>完成，我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep unregister_chrdev_region -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">unregister_chrdev_region</span><span class="params">(<span class="type">dev_t</span> from, <span class="type">unsigned</span> count)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于释放设备号，释放成功后<code>/proc/devices</code>文件对应的记录的主设备号和设备名称消失。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>from</code>：<code>dev_t</code>类型，已成功分配的设备号，就是我们需要释放掉的设备号。</li>
<li><code>count</code>：<code>unsigned</code>类型，申请成功的设备数量。</li>
</ul>
<p><strong>【返回值】</strong><code>none</code></p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/fs.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line">unregister_chrdev_region(devno, <span class="number">1</span>);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h1 id="四、设备注册"><a href="#四、设备注册" class="headerlink" title="四、设备注册"></a><font size=3>四、设备注册</font></h1><h2 id="1-字符设备结构"><a href="#1-字符设备结构" class="headerlink" title="1. 字符设备结构"></a><font size=3>1. 字符设备结构</font></h2><p> 在 <code>Linux</code> 中使用 <code>cdev</code> 结构体表示一个字符设备， <code>cdev</code> 结构体在 <code>include/linux/cdev.h</code> 文件中的定义如下 :</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">cdev</span> &#123;</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">module</span> *<span class="title">owner</span>;</span></span><br><span class="line">        <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> *<span class="title">ops</span>;</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">list</span>;</span></span><br><span class="line">        <span class="type">dev_t</span> dev;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">int</span> count;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><strong>【成员介绍】</strong></p>
<ul>
<li><code>kobj</code>：<code>struct kobject</code>类型，表示该类型实体是一种内核对象。</li>
<li><code>owner</code>：<code>struct module *</code>类型，一般填<code>THIS_MODULE</code>，表示该字符设备从属于哪个内核模块。</li>
<li><code>ops</code>：<code>struct file_operations *</code>类型，指向空间存放着针对该设备的各种操作函数地址，这样建立起来了设备驱动的函数与系统调用的对应关系，如应用程序调用<code>open()</code>函数的时候，对应的系统调用就会找到我们驱动程序中实现的<code>myopen()</code>函数。</li>
<li><code>list</code>：<code>struct list_head</code>类型，表示链表指针域。</li>
<li><code>dev</code>：<code>dev_t</code>类型，表示设备号。</li>
<li><code>count</code>：<code>unsigned int</code>类型，表示设备数量。</li>
</ul>
<p>编写字符设备驱动之前需要定义一个 <code>cdev</code> 结构体变量，这个变量就表示一个字符设备，如下所示：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">cdev</span> <span class="title">mydev</span>;</span></span><br></pre></td></tr></table></figure>

<p>其实我们还有一种方式定义一个设备，就是动态申请（暂时没用过，先写在这里，知道有这么一个函数）：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span>  cdev * <span class="title function_">cdev_alloc</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【说明】</strong>目前按我自己的理解，就是这个结构体对象是用于负责建立系统调用与设备驱动中函数集关系，并且向内核注册这个字符设备。</p>
<h2 id="2-字符设备操作函数集"><a href="#2-字符设备操作函数集" class="headerlink" title="2. 字符设备操作函数集"></a><font size=3>2. 字符设备操作函数集</font></h2><p>我们需要给设备指定操作函数集，这样内核在进行系统调用的时候才知道去调用驱动中的哪一个函数，所以我们需要使用<code>struct file_operations</code>定义一个操作函数集，之后再赋值给<code>cdev</code>变量，我们常用的成员有下边这几个：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">module</span> *<span class="title">owner</span>;</span>                           <span class="comment">//填THIS_MODULE，表示该结构体对象从属于哪个内核模块</span></span><br><span class="line">   <span class="type">int</span> (*open) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *);    <span class="comment">//打开设备</span></span><br><span class="line">   <span class="type">int</span> (*release) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *); <span class="comment">//关闭设备</span></span><br><span class="line">   <span class="type">ssize_t</span> (*read) (<span class="keyword">struct</span> file *, <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);	       <span class="comment">//读设备</span></span><br><span class="line">   <span class="type">ssize_t</span> (*write) (<span class="keyword">struct</span> file *, <span class="type">const</span> <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);    <span class="comment">//写设备</span></span><br><span class="line">   <span class="type">loff_t</span> (*llseek) (<span class="keyword">struct</span> file *, <span class="type">loff_t</span>, <span class="type">int</span>);		                       <span class="comment">//定位</span></span><br><span class="line">   <span class="type">long</span> (*unlocked_ioctl) (<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">long</span>); <span class="comment">//读写设备参数，读设备状态、控制设备</span></span><br><span class="line">   <span class="type">unsigned</span> <span class="title function_">int</span> <span class="params">(*poll)</span> <span class="params">(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> poll_table_struct *)</span>;	<span class="comment">//POLL机制，实现多路复用的支持</span></span><br><span class="line">   <span class="type">int</span> (*mmap) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> vm_area_struct *); <span class="comment">//映射内核空间到用户层</span></span><br><span class="line">   <span class="type">int</span> (*fasync) (<span class="type">int</span>, <span class="keyword">struct</span> file *, <span class="type">int</span>);              <span class="comment">//信号驱动</span></span><br><span class="line">   <span class="comment">//......</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>所以我们定义的函数集如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 操作函数集定义 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> <span class="title">myops</span> =</span> &#123;</span><br><span class="line">    .owner = THIS_MODULE,</span><br><span class="line">    .open = mydev_open,</span><br><span class="line">    .release = mydev_close,</span><br><span class="line">    .read = mydev_read,</span><br><span class="line">    .write = mydev_write,</span><br><span class="line">    .unlocked_ioctl = mydev_ioctl,</span><br><span class="line">    .poll = mydev_poll,</span><br><span class="line">    .fasync = mydev_fasync,</span><br><span class="line">    <span class="comment">// ... ...</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="3-初始化字符设备变量"><a href="#3-初始化字符设备变量" class="headerlink" title="3. 初始化字符设备变量"></a><font size=3>3. 初始化字符设备变量</font></h2><p>定义好 <code>cdev</code> 变量以后就要使用 <code>cdev_init</code> 函数对其进行初始化 ，其实主要为它设置操作函数集。</p>
<h3 id="3-1-owner成员"><a href="#3-1-owner成员" class="headerlink" title="3.1 owner成员"></a><font size=3>3.1 <code>owner</code>成员</font></h3><p>初始化的时候，该成员也需要初始化，一般设置为<code>THIS_MODULE  </code>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mydev.owner = THIS_MODULE;</span><br></pre></td></tr></table></figure>

<h3 id="3-2-cdev-init"><a href="#3-2-cdev-init" class="headerlink" title="3.2 cdev_init()"></a><font size=3>3.2 <code>cdev_init()</code></font></h3><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep cdev_init -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">cdev_init</span><span class="params">(<span class="keyword">struct</span> cdev *cdev, <span class="type">const</span> <span class="keyword">struct</span> file_operations *fops)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于初始化<code>cdev</code> 变量，主要是为该设备添加操作函数集。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>cdev</code>：<code>struct cdev *</code>类型，要初始化的 <code>cdev</code> 结构体变量。</li>
<li><code>fops</code>：<code>struct file_operations *</code>类型，字符设备文件操作函数集合。</li>
</ul>
<p><strong>【返回值】</strong><code>none</code>类型。  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line">cdev_init(&amp;mydev, &amp;myops);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h3 id="3-3-cdev-add"><a href="#3-3-cdev-add" class="headerlink" title="3.3 cdev_add()"></a><font size=3>3.3 <code>cdev_add()</code></font></h3><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep cdev_add -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">cdev_add</span><span class="params">(<span class="keyword">struct</span> cdev *p, <span class="type">dev_t</span> dev, <span class="type">unsigned</span> count)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于将指定字符设备添加到<code>Linux</code>内核，添加成功后会在<code> /proc/devices</code>文件中创建一个包含主设备号和对应的设备名称记录。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>p</code>：<code>struct cdev *</code>类型，指向要添加的字符设备(<code>cdev</code> 结构体变量)  。</li>
<li><code>dev</code>：<code>dev_t</code>类型，设备所使用的设备号。</li>
<li><code>count</code>：<code>unsigned</code>类型，要添加的设备数量，一般填<code>1</code>。</li>
</ul>
<p><strong>【返回值】</strong><code>int</code>类型，添加成功返回 <code>0</code>，失败返回错误码。  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line">cdev_add(&amp;mydev, devno, <span class="number">1</span>); </span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h3 id="3-4-cdev-del"><a href="#3-4-cdev-del" class="headerlink" title="3.4 cdev_del()"></a><font size=3>3.4 <code>cdev_del()</code></font></h3><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep cdev_del -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">cdev_del</span><span class="params">(<span class="keyword">struct</span> cdev *p)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于将指定字符设备从<code>Linux</code>内核移除，移除成功后会删除在<code> /proc/devices</code>文件中创建的包含主设备号和对应设备名称的记录。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>p</code>：<code>struct cdev *</code>类型，指向要移除的字符设备(<code>cdev</code> 结构体变量)  。</li>
</ul>
<p><strong>【返回值】</strong><code>none</code>类型。  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/cdev.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line">cdev_del(&amp;mydev);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong><code>none</code></p>
<h1 id="五、设备节点创建"><a href="#五、设备节点创建" class="headerlink" title="五、设备节点创建"></a><font size=3>五、设备节点创建</font></h1><p>前边的介绍中，我们了解到加载完驱动后，我们后边操作的是<code>/dev/</code>下的设备文件，而这个文件则需要我们来进行创建，所以在申请完设备号之后，我们还需要在<code>/dev</code>目录下创建一个与之对应的设备节点文件， <strong>应用程序将会通过操作这个设备节点文件来完成对具体设备的操作</strong>。   </p>
<h2 id="1-手动创建设备节点"><a href="#1-手动创建设备节点" class="headerlink" title="1. 手动创建设备节点"></a><font size=3>1. 手动创建设备节点</font></h2><p>我们可以通过<code>mknod</code>命令完成设备节点的创建，使用格式如下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mknod /dev/设备文件名 设备种类(c为字符设备,b为块设备) 主设备号 次设备号 # ubuntu下需加sudo执行</span><br></pre></td></tr></table></figure>

<h2 id="2-应用程序创建设备节点"><a href="#2-应用程序创建设备节点" class="headerlink" title="2. 应用程序创建设备节点"></a><font size=3>2. 应用程序创建设备节点</font></h2><h3 id="2-1-mknod"><a href="#2-1-mknod" class="headerlink" title="2.1 mknod()"></a><font size=3>2.1 <code>mknod()</code></font></h3><p>我们还可以通过应用程序来创建设备节点，我们在应用程序中使用<code>mknod</code>系统调用函数来完成设备节点的创建，我们可以使用<code>man 2 mknode</code>来查看函数的帮助手册：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mknod</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *pathname, <span class="type">mode_t</span> mode, <span class="type">dev_t</span> dev)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于创建设备节点，详情可以查看帮助手册，这个函数我没怎么用过，这里做一个简单的笔记。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>pathname</code>：<code>char *</code>类型，带路径的设备文件名，无路径默认为当前目录，一般都创建在<code>/dev</code>下。</li>
<li><code>mode</code>：<code>mode_t</code>类型，文件权限。  </li>
<li><code>dev</code>：<code>dev_t</code>类型，<code>32</code>位设备号。</li>
</ul>
<p><strong>【返回值】</strong><code>int</code>类型，成功返回<code>0</code>，失败返回<code>-1</code>。</p>
<p><strong>【使用格式】</strong><code>none</code></p>
<p><strong>【注意事项】</strong><code>none</code></p>
<h2 id="3-自动创建设备节点"><a href="#3-自动创建设备节点" class="headerlink" title="3. 自动创建设备节点"></a><font size=3>3. 自动创建设备节点</font></h2><p>前边两种不免比较麻烦，增加了应用程序开发人员的负担，我们其实是可以在加载驱动模块成功的时候自动在<code>/dev</code>目录下<br>创建对应的设备文件。</p>
<h3 id="3-1-mdev机制"><a href="#3-1-mdev机制" class="headerlink" title="3.1 mdev机制"></a><font size=3>3.1 <code>mdev</code>机制</font></h3><p><code>udev</code> 是一个用户程序，在 <code>Linux</code> 下通过 <code>udev</code> 来实现设备文件的创建与删除， <code>udev</code> 可以检测系统中硬件设备状态，可以根据系统中硬件设备状态来创建或者删除设备文件。比如使用<code>insmod</code>命令成功加载驱动模块以后就自动在<code>/dev</code>目录下创建对应的设备节点文件，使用<code>rmmod</code>命令卸载驱动模块以后就删除掉<code>/dev</code>目录下的设备节点文件。 使用<code>busybox</code>构建根文件系统的时候，<code>busybox</code>会创建一个 <code>udev</code>的简化版本——<code>mdev</code>。</p>
<p>内核中定义了<code>struct class</code>结构体，一个<code>struct class</code>结构体类型变量对应一个类， 内核同时提供了<code>class_create()</code>函数，可以用它来创建一个类，这个类存放于<code>/sys/class</code>下面，一旦创建好了这个类，再调用<code>device_create()</code>函数来在<code>/dev</code>目录下创建相应的设备节点。这样，加载模块的时候，用户空间中的<code>udev</code>会自动响应<code>device_create()</code>函数，去<code>/sys/class</code>下寻找对应的类从而创建设备节点，需要注意的是，当只有<strong>一个主设备号，多个次设备号的时候，这些次设备的类只有一个</strong>。</p>
<p>所以在嵌入式 <code>Linux</code> 中我们使用<code>mdev</code>来实现设备节点文件的自动创建与删除，<code>Linux</code>系统中的热插拔事件也由<code>mdev</code>管理，在<code>/etc/init.d/rcS</code>文件中添加如下语句：  </p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo /sbin/mdev &gt; /proc/sys/kernel/hotplug # 命令设置热插拔事件由 mdev 来管理</span><br></pre></td></tr></table></figure>

<h3 id="3-2-创建和删除类"><a href="#3-2-创建和删除类" class="headerlink" title="3.2 创建和删除类"></a><font size=3>3.2 创建和删除类</font></h3><h4 id="3-2-1-class-create"><a href="#3-2-1-class-create" class="headerlink" title="3.2.1 class_create()"></a><font size=3>3.2.1 <code>class_create()</code></font></h4><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep class_create -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="keyword">class</span> * __<span class="title">must_check</span> __<span class="title">class_create</span>(<span class="keyword">struct</span> <span class="title">module</span> *<span class="title">owner</span>,</span></span><br><span class="line"><span class="class">                                                  <span class="title">const</span> <span class="title">char</span> *<span class="title">name</span>,</span></span><br><span class="line"><span class="class">                                                  <span class="keyword">struct</span> <span class="title">lock_class_key</span> *<span class="title">key</span>);</span></span><br><span class="line"><span class="comment">/* ... ... */</span>											  </span><br><span class="line"><span class="meta">#<span class="keyword">define</span> class_create(owner, name)               \</span></span><br><span class="line"><span class="meta">(&#123;                                              \</span></span><br><span class="line"><span class="meta">        static struct lock_class_key __key;     \</span></span><br><span class="line"><span class="meta">        __class_create(owner, name, &amp;__key);    \</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于创建一个类，在<code>/sys/class</code>生成一个目录，目录名由<code>name</code>指定（会得到一个<code>/sys/class/name</code>目录）。它其实是个宏定义，展开后内容如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> class *<span class="title function_">class_create</span><span class="params">(<span class="keyword">struct</span> module *owner, <span class="type">const</span> <span class="type">char</span> *name)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数参数】</strong></p>
<ul>
<li><code>owner</code>：<code>struct module *</code>类型，一般为<code>THIS_MODULE </code>。</li>
<li><code>name</code>：<code>char *</code>类型，类的名字。</li>
</ul>
<p><strong>【返回值】</strong><code>struct class *</code>类型，是一个指向结构体<code>class</code>的指针，也就是创建的类，失败的时候返回<code>NULL</code>。  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="keyword">class</span> *<span class="keyword">class</span>;</span>   <span class="comment">/* 类 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> =</span> class_create(THIS_MODULE, <span class="string">&quot;dev_name&quot;</span>);</span><br><span class="line"><span class="keyword">if</span> (IS_ERR(class))</span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> PTR_ERR(class);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong></p>
<p>（1）辅助接口：可以定义一个<code>struct class</code>的指针变量<code>class</code>来接受返回值，然后通过<code>IS_ERR(class)</code>判断是否失败；</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">IS_ERR(device);<span class="comment">/* 成功--&gt;0，失败--&gt;非0 */</span></span><br><span class="line">PTR_ERR(cls); <span class="comment">/* 获得失败的返回错误码 */</span></span><br></pre></td></tr></table></figure>

<p>（2）主设备号相同，次设备号不同的几个次设备，他们属于同一个类，只需要创建一个类即可。</p>
<h4 id="3-2-2-class-destroy"><a href="#3-2-2-class-destroy" class="headerlink" title="3.2.2 class_destroy()"></a><font size=3>3.2.2 <code>class_destroy()</code></font></h4><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep class_destroy -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">class_destroy</span><span class="params">(<span class="keyword">struct</span> class *cls)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于删除一个类，会删除创建类时在<code>/sys/class</code>生成的那个目录（会删除<code>/sys/class/name</code>目录）。</p>
<p><strong>【函数参数】</strong></p>
<ul>
<li><code>cls</code>：<code>struct class *</code>类型，要删除的类。</li>
</ul>
<p><strong>【返回值】</strong><code>none</code>  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="keyword">class</span> *<span class="keyword">class</span>;</span>   <span class="comment">/* 创建类时定义 */</span></span><br><span class="line"></span><br><span class="line">class_destroy(class);  <span class="comment">/* 删除类 */</span></span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong></p>
<p>（1）主设备号相同，次设备号不同的几个次设备，他们属于同一个类，只需要创建一个类，删除的时候也只需要删除创建的那一个类。</p>
<h3 id="3-3创建和删除设备"><a href="#3-3创建和删除设备" class="headerlink" title="3.3创建和删除设备"></a><font size=3>3.3创建和删除设备</font></h3><p>创建完了类之后，我们还需要在<code>/dev/</code>下创建我们的设备节点。</p>
<h4 id="3-3-1-device-create"><a href="#3-3-1-device-create" class="headerlink" title="3.3.1 device_create()"></a><font size=3>3.3.1 <code>device_create()</code></font></h4><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep device_create -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="keyword">struct</span> device *<span class="title function_">device_create</span><span class="params">(<span class="keyword">struct</span> class *cls, <span class="keyword">struct</span> device *parent,</span></span><br><span class="line"><span class="params">                             <span class="type">dev_t</span> devt, <span class="type">void</span> *drvdata,</span></span><br><span class="line"><span class="params">                             <span class="type">const</span> <span class="type">char</span> *fmt, ...)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于在<code>/sys/class</code>目录下<code>class_create</code>生成目录<strong>再生成一个子目录与该设备相对应</strong>，<code>uevent</code>让应用程序<code>udevd</code>创建设备文件，最终会创建<code>/dev/fmt</code>作为我们的设备节点。该函数最终创建的文件有：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">/sys/class/class_name/fmt # 目录</span><br><span class="line">/dev/fmt                  # 设备节点文件</span><br></pre></td></tr></table></figure>

<p><strong>【函数参数】</strong></p>
<ul>
<li><code>cls</code>：<code>struct class *</code>类型，创建的类名，表示设备要创建哪个类下面。</li>
<li><code>parent</code>：<code>struct device *</code>类型，表示父设备，一般为<code>NULL</code>，也就是没有父设备。</li>
<li><code>devt</code>：<code>dev_t</code>类型，已经分配成功的设备号。  </li>
<li><code>drvdata</code>：<code>void *</code>类型，驱动私有数据，一般为<code>NULL</code>。</li>
<li><code>fmt</code>：<code>char *</code>类型，表示设备名字 ，是一个格式化字符串，类似<code>printf</code>，可以是一个固定的字符串，也可以是格式化字符串，若<code>fmt=xxx</code> ，就会生成<code>/dev/xxx</code>这个设备文件。  若为格式化字符串<code>%s%d</code>，则需要后边的<code>vargs</code>参数。</li>
<li><code>...</code>：不定参数列表，前边的<code>fmt</code>若为<code>&quot;%s%d&quot;</code>，这里就需要一个字符串和一个整型变量，就类似于<code>printf</code>。</li>
</ul>
<p><strong>【返回值】</strong><code>struct device *</code>类型，是一个指向结构体<code>device </code>的指针，也就是表示创建好的设备，失败的时候返回<code>NULL</code>。  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">device</span> *<span class="title">device</span>;</span> <span class="comment">/* 设备 */</span></span><br><span class="line">device = device_create(class, <span class="literal">NULL</span>, devno, <span class="literal">NULL</span>, <span class="string">&quot;%s%d&quot;</span>, <span class="string">&quot;dev_name&quot;</span>, num);</span><br><span class="line"><span class="keyword">if</span> (IS_ERR(device))</span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> PTR_ERR(device);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong></p>
<p>（1）辅助接口：可以定义一个<code>struct device</code>的指针变量<code>device</code>来接受返回值，然后通过<code>IS_ERR(device)</code>判断是否失败；</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">IS_ERR(device);<span class="comment">/* 成功--&gt;0，失败--&gt;非0 */</span></span><br><span class="line">PTR_ERR(device); <span class="comment">/* 获得失败的返回错误码 */</span></span><br></pre></td></tr></table></figure>

<p>（2）主设备号相同，次设备号不同的几个次设备，他们属于同一个类，只需要创建一个类，需要创建多个设备对应不同的次设备。</p>
<h4 id="3-2-2-device-destroy"><a href="#3-2-2-device-destroy" class="headerlink" title="3.2.2 device_destroy()"></a><font size=3>3.2.2 <code>device_destroy()</code></font></h4><p>我们使用以下命令查询一下函数所在头文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep device_destroy -r -n ~/5linux/linux-3.14/include</span><br></pre></td></tr></table></figure>

<p>经过查找，我们可以得到如下信息：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 函数声明 */</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">device_destroy</span><span class="params">(<span class="keyword">struct</span> class *cls, <span class="type">dev_t</span> devt)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>【函数说明】</strong>该函数用于删除一个设备，会删除创建设备时在<code>/sys/class</code>生成的那个目录。就是说下边目录和文件会被删除：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">/sys/class/class_name/fmt # 目录</span><br><span class="line">/dev/fmt                  # 设备节点文件</span><br></pre></td></tr></table></figure>

<p><strong>【函数参数】</strong></p>
<ul>
<li><code>cls</code>：<code>struct class *</code>类型，要删除的设备所处的类。</li>
<li><code>devt</code>：<code>dev_t</code>类型，表示要删除的设备号。</li>
</ul>
<p><strong>【返回值】</strong><code>none</code>  </p>
<p><strong>【使用格式】</strong>一般情况下基本使用格式如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 需要包含的头文件 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/device.h&gt;</span></span></span><br><span class="line"><span class="comment">/* 至少应该有的语句 */</span></span><br><span class="line">device_destroy(class, devno);</span><br></pre></td></tr></table></figure>

<p><strong>【注意事项】</strong></p>
<p>（1）主设备号相同，次设备号不同的几个次设备，他们属于同一个类，只需要创建一个类，需要创建多个设备对应不同的次设备，删除的时候就需要将这些设备逐个删除。</p>
<p>（2）删除的时候可以先删除设备，再删除类，按理说可以直接删除类就完事了，毕竟类目录都没了，子目录应该也就一起删除了。</p>

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/f0272df3.html">LV10-02-字符设备驱动-01-基础知识</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2023年07月08日 - 18:58</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/f0272df3.html" title="LV10-02-字符设备驱动-01-基础知识">https://sumumm.github.io/post/f0272df3.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/LV10-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/" rel="tag"><i class="fa fa-tag"></i> LV10-驱动开发</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/7daeed19.html" rel="prev" title="LV10-02-字符设备驱动-02-基本驱动函数实现">
                  <i class="fa fa-angle-left"></i> LV10-02-字符设备驱动-02-基本驱动函数实现
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/62647cd9.html" rel="next" title="LV10-01-内核模块-02-模块参数与依赖">
                  LV10-01-内核模块-02-模块参数与依赖 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
